/* Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "luafilter/l_callbacks.h"
#include "common.h"

void LCallbacks::register_to(lua_State *L, luawrap::stack_table &t)
{
    luawrap::restore_stack_top _s(L);

    auto cls = luawrap::lua_class<LCallbacks>(L);
    cls.ctor<>();
    cls.def("set_send_recv_cb", c_func_wrap(L, LCallbacks::set_send_recv_cb));

    t.set("callbacks", cls);
}

void LCallbacks::set_send_recv_cb(const string& device_name, optional<t_send_recv_cb> cb)
{
    auto it = cb_map.find(device_name);
    if (it == cb_map.end()) {
        unique_ptr<st_cb> s_cb = make_unique<LCallbacks::st_cb>();
        cb_map.emplace(device_name, move(s_cb));
        it = cb_map.find(device_name);
        if (it == cb_map.end()) {
            return;
        }
    }

    auto &s_cb = *it->second;
    unique_lock<mutex> guard(s_cb.send_recv_mutex);
    s_cb.send_recv_cb = move(cb);
}

int32_t LCallbacks::send_recv(const string& device_name, const uint8_t *request, size_t request_size,
    uint8_t *response, size_t *response_size)
{
    auto it = cb_map.find(device_name);
    if (it == cb_map.end()) {
        debug_log(DLOG_ERROR, "can not found send/recv callback");
        return RET_ERR;
    }

    auto &s_cb = *it->second;
    unique_lock<mutex> guard(s_cb.send_recv_mutex);
    if (!s_cb.send_recv_cb) {
        debug_log(DLOG_ERROR, "can not found send/recv callback");
        return RET_ERR;
    }

    string_view req_data(reinterpret_cast<const char *>(request), request_size);

    int32_t               result;
    optional<string_view> rsp_data;
    uint8_t               wait_rsp = 0;
    uint32_t              timeout = 0;
    tie(result, rsp_data) = (*s_cb.send_recv_cb)(device_name, req_data, wait_rsp, timeout);

    if (result != RET_OK) {
        debug_log(DLOG_ERROR, "send message failed, result: %d", result);
        return RET_ERR;
    }

    result = memcpy_s(response, *response_size, rsp_data->data(), rsp_data->size());
    if (result != RET_OK) {
        debug_log(DLOG_ERROR, "memcpy_s failed, ret = %d", result);
        *response_size = 0;
    } else {
        *response_size = rsp_data->size();
    }

    return result;
}

unordered_map<string, unique_ptr<LCallbacks::st_cb>> LCallbacks::cb_map;